package edu.maping.easy;

import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;

import napplet.EMNAppletManager;
import napplet.NApplet;

import controlP5.Bang;
import controlP5.ControlGroup;

import edu.maping.easy.composer.Composer;
import edu.maping.easy.editor.EasyMappingEditor;
import edu.maping.easy.fisica.EasyMappingFisica;
import edu.maping.easy.gui.Button;
import edu.maping.easy.gui.CircleButton;
import edu.maping.easy.gui.GUIEvent;
import edu.maping.easy.gui.GuiFactory;
import edu.maping.easy.gui.ScreenLogger;
import edu.maping.easy.gui.Slider;
import edu.maping.easy.io.KeyBoardInteraction;
import edu.maping.easy.minim.MinimPlugin;
import edu.maping.easy.model.EasyMappingPlugin;
import edu.maping.easy.model.GUIInteraction;
import edu.maping.easy.model.MouseInteraction;
import edu.maping.easy.model.MouseWheelInput;
import edu.maping.easy.model.PolygonThing;
import edu.maping.easy.model.Thing;
import edu.maping.easy.model.World;
import edu.maping.easy.painter.PainterDelegateEditor;
import edu.maping.easy.painter.PolygonRendererManager;
import edu.maping.easy.painter.ThingPainterDelegate;
import edu.maping.easy.painter.napplet.EMNAppletAdapter;
import edu.maping.easy.painter.plugins.FireCube;
import edu.maping.easy.painter.plugins.FisicaRenderer;
import edu.maping.easy.painter.plugins.FotoViewer;
import edu.maping.easy.painter.plugins.ThingPainterContournTransform;
import edu.maping.easy.painter.plugins.ThingPainterRectangleTransform;
import edu.maping.easy.painter.plugins.ThingPainterRectangleTransform3D;
import edu.maping.easy.sequence.PropagationManager;
import edu.maping.easy.sequence.Sequencer;
import edu.maping.easy.sequence.ticker.CollisionTicker;
import edu.maping.easy.sequence.ticker.TimeTicker;
import fisica.FBody;

import processing.core.PApplet;
import processing.core.PFont;
import processing.xml.XMLElement;
import processing.xml.XMLWriter;

public class EasyMapping implements MouseListener, KeyListener,
		MouseMotionListener {

	PApplet pApplet;

	World world = new World();

	ArrayList<EasyMappingPlugin> plugins = new ArrayList<EasyMappingPlugin>();
	ArrayList<MouseInteraction> mouseInteractions = new ArrayList<MouseInteraction>();
	ArrayList<GUIInteraction> controlP5Interactions = new ArrayList<GUIInteraction>();

	EasyMappingFisica fisicaPlugin = null;
	ControlManager controlManager = null;
	EasyMappingEditor easyMappingEditor = null;
	GuiFactory guiFactory = null;

	PolygonRendererManager painterManager = new PolygonRendererManager();
	ScreenLogger screenLogger = null;
	Sequencer sequencer = null;
	KeyBoardInteraction keyBoardInteraction = new KeyBoardInteraction();

	int panelX = 0;
	int panelY = 15;

	boolean editDraw = false;

	EMNAppletManager emnAppletManager;

	public EasyMapping(PApplet pApplet) {
		setup(pApplet);
	}

	protected void setup(PApplet pApplet) {

		this.pApplet = pApplet;

		pApplet.addMouseListener(this);
		pApplet.addMouseMotionListener(this);
		pApplet.registerDraw(this);
		pApplet.addKeyListener(this);

		pApplet.rectMode(PApplet.CORNERS);

		emnAppletManager = new EMNAppletManager(pApplet);

		/* CONSTRUCCION DE PLUGINS */
		try {
			PFont font = pApplet.loadFont("Ziggurat-HTF-Black-32.vlw");
			pApplet.textFont(font, 9);
			controlManager = initControlPAnel();
			MouseWheelInput wheelInput = initMouseWheelListener();
			initPlugins(wheelInput);
			addPainterDelegates();
			initControlPanelNew();
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	public void createEmbeddedNApplet(String nappletClassName, int x, int y) {
		emnAppletManager.createEmbeddedNApplet(nappletClassName, x, y);
	}

	private MouseWheelInput initMouseWheelListener() {
		MouseWheelInput mouseWheelInput = new MouseWheelInput();
		pApplet.frame.addMouseWheelListener(mouseWheelInput);
		return mouseWheelInput;
	}

	private void initPlugins(MouseWheelInput mouseWheelInput) {
		// controlManager = initControlPAnel();

		guiFactory = new GuiFactory();
		guiFactory.setup(pApplet);

		PainterDelegateEditor painterDelegateEditor = new PainterDelegateEditor();
		painterDelegateEditor.guiFactory = guiFactory;

		MinimPlugin minimPlugin = new MinimPlugin();
		minimPlugin.setup(pApplet);
		plugins.add(minimPlugin);

		// fisicaPlugin = new EasyMappingFisica();
		// fisicaPlugin.setup(pApplet, guiFactory);
		// fisicaPlugin.fftFacade = minimPlugin;
		// plugins.add(fisicaPlugin);
		// controlP5Interactions.add(fisicaPlugin);

		/* CONSTRUCCIÓN DE DELEGADOS DE RENDERIZADO DE POLIGONOS */
		sequencer = new Sequencer();
		sequencer.setup(pApplet, world, controlManager, guiFactory);
		sequencer.painterDelegateEditor = painterDelegateEditor;

		plugins.add(sequencer);

		controlP5Interactions.add(sequencer);
		sequencer.rendererManager = painterManager;
		mouseWheelInput.list.add(sequencer);

		minimPlugin.setTimeTicker(sequencer);

		Composer composer = new Composer();
		composer.sequencer = sequencer;
		mouseWheelInput.list.add(composer);

		plugins.add(composer);

		controlP5Interactions.add(composer);
		composer.setup(pApplet, guiFactory);

		screenLogger = new ScreenLogger();
		screenLogger.setup(pApplet);

		easyMappingEditor = new EasyMappingEditor();
		easyMappingEditor.screenLogger = screenLogger;
		easyMappingEditor.setup(pApplet, world, guiFactory);
		plugins.add(easyMappingEditor);
		controlP5Interactions.add(easyMappingEditor);

		sequencer.screenLogger = screenLogger;

		plugins.add(guiFactory);

		mouseInteractions.add(painterDelegateEditor);
		// mouseInteractions.add(fisicaPlugin);
		mouseInteractions.add(sequencer);
		mouseInteractions.add(easyMappingEditor);
		mouseInteractions.add(guiFactory);

		PropagationManager propagationManager = new PropagationManager();
		propagationManager.setGuiFactory(guiFactory);
		propagationManager.setScreenLogger(screenLogger);
		propagationManager.setEasyMappingEditor(easyMappingEditor);

		propagationManager.setup();
		painterManager.propagationManager = propagationManager;

	}

	private EMNAppletAdapter addPainterDelegatesNew(String nappletClassName) {
		
		NApplet nApplet = emnAppletManager.createEmbeddedNApplet(
				nappletClassName, 100, 100);

		EMNAppletAdapter emnAppletAdapter = null;

		if (nApplet instanceof EMNAppletAdapter) {
			emnAppletAdapter = (EMNAppletAdapter) nApplet;
			emnAppletAdapter.stop();
		} else {
			pApplet.println("Napplet is not extended from EMNappletAdapter");
			return null;
		}

		painterManager.addPainter(emnAppletAdapter);
		
		return emnAppletAdapter;
	}

	private void addPainterDelegates() {

		painterManager.setup(pApplet, world, controlManager, guiFactory);
		addPainterDelegatesNew("edu.maping.easy.painter.plugins.FireCube");
		addPainterDelegatesNew("edu.maping.easy.painter.plugins.RectangleTransformEMNApplet");
		addPainterDelegatesNew("edu.maping.easy.painter.plugins.CopyOfFisicaRenderer");

//		ThingPainterContournTransform contourn = new ThingPainterContournTransform();
		ThingPainterRectangleTransform pulso = new ThingPainterRectangleTransform();
		pulso.setNappletManager(emnAppletManager);
//		ThingPainterRectangleTransform3D test2 = new ThingPainterRectangleTransform3D();
//		FotoViewer fotoViewer = new FotoViewer();
//		FireCube fireCube = new FireCube();
//		FisicaRenderer fisicaRenderer = new FisicaRenderer();
//
//		painterManager.addPainter(contourn);
//		painterManager.addPainter(test);
//		painterManager.addPainter(test2);
//		painterManager.addPainter(fotoViewer);
//		painterManager.addPainter(fireCube);
//		painterManager.addPainter(fisicaRenderer);
		painterManager.addPainter(pulso);
		

		TimeTicker timeTicker = new TimeTicker();
		CollisionTicker collisionTicker = new CollisionTicker();
		collisionTicker.fisica = fisicaPlugin;

		painterManager.addTicker(timeTicker);
		painterManager.addTicker(collisionTicker);

	}

	private ControlManager initControlPAnel() {

		ControlManager controlP5 = new ControlManager(pApplet);
		ControlGroup l = controlP5.getGroup();
		controlP5.setMainGroup(l);

		panelX = pApplet.width - 120;

		Bang bang3 = controlP5.addBang("delete", 0, 145, 20, 20);
		bang3.setLabel("DELETE");
		bang3.setGroup(l);

		return controlP5;
	}

	private void initControlPanelNew() {
		edu.maping.easy.gui.Button startVisual = new edu.maping.easy.gui.Button(
				"Start", 30, 10, 50, 20, pApplet);
		guiFactory.add(startVisual);

		edu.maping.easy.gui.Button endVisual = new edu.maping.easy.gui.Button(
				"End", 80, 10, 40, 20, pApplet);
		guiFactory.add(endVisual);

		edu.maping.easy.gui.Button load = new edu.maping.easy.gui.Button(
				"Load", 140, 10, 40, 20, pApplet);
		guiFactory.add(load);

		edu.maping.easy.gui.Button save = new edu.maping.easy.gui.Button(
				"Save", 185, 10, 40, 20, pApplet);
		guiFactory.add(save);
	}

	public void draw() {

		try {

			controlManager.getMainGroup().setPosition(pApplet.width - 100,
					panelY + 10);
			controlManager.getMainGroup().updateAbsolutePosition();

			pApplet.background(0);
			controlManager.draw();
			if (editDraw) {
				world.editDraw(pApplet);
			}

			for (EasyMappingPlugin plugin : plugins) {
				plugin.draw(pApplet);
			}

			screenLogger.draw(pApplet);

		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	public void mouseClicked(MouseEvent event) {

		panelX = pApplet.width - 120;

		for (MouseInteraction plugin : mouseInteractions) {
			boolean b = plugin.mouseClicked(event.getX(), event.getY(),
					easyMappingEditor.getNewThing(), event);
			if (b) {
				return;
			}
		}

	}

	@Override
	public void mouseEntered(MouseEvent arg0) {
		// TODO Auto-generated method stub

	}

	@Override
	public void mouseExited(MouseEvent arg0) {
		// TODO Auto-generated method stub

	}

	@Override
	public void mousePressed(MouseEvent event) {
		for (MouseInteraction plugin : mouseInteractions) {
			plugin.mousePreassed(event.getX(), event.getY(),
					easyMappingEditor.getNewThing());
		}

	}

	public void startVisual() {
		for (EasyMappingPlugin plugin : plugins) {
			plugin.start(world, pApplet, controlManager);
		}
	}

	public void endVisual() {
		for (EasyMappingPlugin plugin : plugins) {
			plugin.stop(world, pApplet, controlManager);
		}
	}

	public void saveToFile() {

		try {

			XMLElement father = new XMLElement();
			father.setName("EasyMappingWorld");

			for (Thing thing : world.world) {
				father.addChild(thing.startXmlization());
			}

			XMLElement seqElement = sequencer.xmlIzation();
			father.addChild(seqElement);

			String file = pApplet.selectOutput();
			if (file == null) {
				return;
			}
			PrintWriter printWriter = pApplet.createWriter(file);
			XMLWriter writer = new XMLWriter(printWriter);

			try {
				writer.write(father);
			} catch (IOException e) {
				e.printStackTrace();
			}

			printWriter.flush();
			printWriter.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void loadFromFile() {
		String file = pApplet.selectInput();
		if (file == null)
			return;
		BufferedReader reader = pApplet.createReader(file);
		XMLElement xmlElement = new XMLElement(reader);
		String name = xmlElement.getName();
		// System.out.println("Leido fichero " + name);
		for (int index = 0; index < xmlElement.getChildCount(); index++) {
			XMLElement child = xmlElement.getChild(index);

			if (child.getName().equals(Thing.XML_NAME)) {
				PolygonThing polygonThing = new PolygonThing();
				polygonThing = polygonThing.startDesXmlization(child);
				// addPainterDelegates(polygonThing);
				world.world.add(polygonThing);
			}

			if (child.getName().equals("Sequences")) {
				try {
					sequencer.desXmlIzation(child);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			// if(child.getName().equals(anObject))
		}
	}

	@Override
	public void mouseReleased(MouseEvent event) {
		GUIEvent guiEvent = new GUIEvent();

		guiFactory.setClicked(null);

		for (MouseInteraction plugin : mouseInteractions) {
			plugin.mouseReleased(event.getX(), event.getY(),
					world.hovered(event.getX(), event.getY()));
		}

		FBody clicked = guiFactory.getHovered(event.getX(), event.getY());
		if (clicked != null && clicked.isDrawable()) {
			if (clicked instanceof Button) {
				Button button = (Button) clicked;
				guiEvent.button = button;
				if (button.name != null && button.name.equals("Start")) {
					startVisual();
				} else if (button.name != null && button.name.equals("End")) {
					endVisual();
				} else if (button.name != null && button.name.equals("Load")) {
					loadFromFile();
				} else if (button.name != null && button.name.equals("Save")) {
					saveToFile();
				} else {
					System.out.println("ERRO");
				}
			}

			if (clicked instanceof Slider) {
				Slider slider = (Slider) clicked;
				slider.setValue(event.getX(), event.getY());
				guiEvent.slider = slider;
			}

			if (clicked instanceof CircleButton) {
				CircleButton circleButton = (CircleButton) clicked;
				guiEvent.circleButton = circleButton;
			}

			propagateEvent(guiEvent);
		}

		guiFactory.setClicked(null);

	}

	public void keyReleased(KeyEvent event) {
		keyBoardInteraction.keyReleased(event);
	}

	public void keyPressed(KeyEvent event) {
		keyBoardInteraction.keyPressed(event);
		// println(KeyEvent.getKeyText(event.getKeyCode()));

		for (MouseInteraction mouseInteraction : mouseInteractions) {
			mouseInteraction.KeyPressed(keyBoardInteraction);
		}

		Thing newThing = easyMappingEditor.getNewThing();

		char key = event.getKeyChar();

		if (newThing != null)
			for (MouseInteraction mouseInteraction : mouseInteractions) {
				mouseInteraction.editKeyPressed(key, newThing);
			}

		if (key == 'p') {
			saveToFile();
		}

		if (key == 'l') {
			loadFromFile();
		}

		if (key == 'e' || key == 'E') {
			editDraw = !editDraw;
		}
	}

	@Override
	public void keyTyped(KeyEvent arg0) {
		// TODO Auto-generated method stub

	}

	private void propagateEvent(GUIEvent event) {
		for (GUIInteraction plugin : controlP5Interactions) {

			try {
				plugin.controlEvent(event);
			} catch (Exception e) {
				e.printStackTrace();
				System.out.println("ERROR EN EL PLUGIN " + plugin.toString());
			}
		}
	}

	@Override
	public void mouseDragged(MouseEvent event) {

		for (MouseInteraction plugin : mouseInteractions) {
			plugin.mouseDragged(event.getX(), event.getY(),
					easyMappingEditor.getNewThing());
		}

		FBody clicked = guiFactory.getHovered();

		if (clicked == null || !clicked.isDrawable()) {
			return;
		}

		if (clicked instanceof Slider) {
			Slider slider = (Slider) clicked;
			slider.setValue(event.getX(), event.getY());

			GUIEvent eventGui = new GUIEvent();
			eventGui.slider = slider;
			propagateEvent(eventGui);
		}

		guiFactory.setClicked(null);

	}

	@Override
	public void mouseMoved(MouseEvent event) {
		for (MouseInteraction plugin : mouseInteractions) {
			plugin.mouseMoved(event.getX(), event.getY());
		}

	}
}
